package com.fand.persistent.support;

import java.util.List;
import java.util.Map;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import org.apache.commons.lang.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.util.CollectionUtils;

import com.fand.dto.PaginationDto;
import com.fand.persistent.builder.ModelBuilder;
import com.fand.template.support.TemplateSupport;
import com.fand.utils.PaginationUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * jdbc dao
 * 
 * @author yaoHT
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class DaoJdbcSupport implements DaoSupport {
	/**
	 * namedParameterJdbcTemplateRouterSupport
	 */
	private NamedParameterJdbcTemplateRouterSupport namedParameterJdbcTemplateRouterSupport;

	/**
	 * 模板支持
	 */
	private TemplateSupport templateSupport;

	@Override
	public String builderSql(final String sqlId, final Map<String, Object> model) {
		final String sql = this.templateSupport.processTemplateIntoString(sqlId, model);

		return sql;
	}

	@Override
	public PaginationDto<Map<String, Object>> paginationForMapList(final String selectForObjectSqlId,
			final String selectForMapListSqlId, final int paginationNo, final int paginationSize,
			Map<String, Object> model) {
		final int totalSize = this.selectForObject(Integer.class, selectForObjectSqlId, null, model);

		final PaginationDto<Map<String, Object>> pagination = PaginationUtils.newPagination(paginationNo,
				paginationSize, totalSize);

		List<Map<String, Object>> dataList;

		if (totalSize < 1) {
			dataList = Lists.newArrayList();
		} else {
			if (totalSize < pagination.getBeginRowNum()) {
				dataList = Lists.newArrayList();
			} else {
				if (model == null) {
					model = Maps.newHashMap();
				}

				model.put("BEGIN__ROW__NUM", pagination.getBeginRowNum());

				model.put("END__ROW__NUM", pagination.getEndRowNum());

				dataList = this.selectForMapList(selectForMapListSqlId, model);
			}
		}

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public <R> PaginationDto<R> paginationForMappedObjectList(final Class<R> mappedClass,
			final String selectForObjectSqlId, final String selectForMappedObjectListSqlId, final int paginationNo,
			final int paginationSize, Map<String, Object> model) {
		final int totalSize = this.selectForObject(Integer.class, selectForObjectSqlId, null, model);

		final PaginationDto<R> pagination = PaginationUtils.newPagination(paginationNo, paginationSize, totalSize);

		List<R> dataList;

		if (totalSize < 1) {
			dataList = Lists.newArrayList();
		} else {
			if (totalSize < pagination.getBeginRowNum()) {
				dataList = Lists.newArrayList();
			} else {
				if (model == null) {
					model = Maps.newHashMap();
				}

				model.put("BEGIN__ROW__NUM", pagination.getBeginRowNum());

				model.put("END__ROW__NUM", pagination.getEndRowNum());

				dataList = this.selectForMappedObjectList(mappedClass, selectForMappedObjectListSqlId, model);
			}
		}

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public Map<String, Object> selectForMap(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model).queryForMap(
				sql, model);
	}

	@Override
	public List<Map<String, Object>> selectForMapList(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model)
				.queryForList(sql, model);
	}

	@Override
	public <R> R selectForMappedObject(final Class<R> mappedClass, final String sqlId,
			final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);

		try {
			return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model)
					.queryForObject(sql, model, ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
		} catch (final EmptyResultDataAccessException e) {
			return null;
		}
	}

	@Override
	public <R> List<R> selectForMappedObjectList(final Class<R> mappedClass, final String sqlId,
			final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model).query(sql,
				model, ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
	}

	@Override
	public <R> R selectForObject(final Class<R> requiredType, final String sqlId, final String objectId,
			Map<String, Object> model) {
		if (StringUtils.isNotBlank(objectId)) {
			if (model == null) {
				model = Maps.newHashMap();
			}

			model.put(ModelBuilder.OBJECT_ID, objectId);
		}

		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model)
				.queryForObject(sql, model, requiredType);
	}

	@Override
	public <R> List<R> selectForObjectList(final Class<R> elementType, final String sqlId, final String objectId,
			Map<String, Object> model) {
		if (StringUtils.isNotBlank(objectId)) {
			if (model == null) {
				model = Maps.newHashMap();
			}

			model.put(ModelBuilder.OBJECT_ID, objectId);
		}

		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model)
				.queryForList(sql, model, elementType);
	}

	@Override
	public int update(final String sqlId, final List<Map<String, Object>> modelList) {
		if (CollectionUtils.isEmpty(modelList)) {
			return 0;
		}

		int rowCount = 0;

		for (final Map<String, Object> model : modelList) {
			rowCount += this.update(sqlId, model);
		}

		return rowCount;
	}

	@Override
	public int update(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);

		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(sqlId, model).update(sql,
				model);
	}
}
